package com.example.chat.websocket;

import com.alibaba.fastjson.JSON;
import com.example.chat.chat.pojo.dto.ChatDTO;
import com.example.chat.chat.service.ChatService;
import com.example.chat.common.constant.DigitConstant;
import com.example.chat.common.constant.NettyConstant;
import com.example.chat.common.context.SpringContext;
import com.example.chat.receptionist.pojo.dto.ReceptionistDTO;
import com.example.chat.receptionist.service.ReceptionistService;
import com.example.chat.session.pojo.dto.SessionDTO;
import com.example.chat.session.service.SessionService;
import com.example.chat.visitor.service.VisitorService;
import com.example.chat.websocket.dto.MessageDTO;
import com.example.chat.websocket.dto.ResultDTO;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.util.Objects;

@Component
@Slf4j
public class NettyTextHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    public static final String USER_ID = "userId";
    public static final String ROLE = "role";

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        String text = msg.text();
        if (StringUtils.isBlank(text)) { return; }

        MessageDTO messageDTO = JSON.parseObject(text, MessageDTO.class);

        if (messageDTO.getAction().equals("chat")) {
            this.sendMsg(ctx, messageDTO);
        } else if (messageDTO.getAction().equals("init")) {
            this.register(ctx, messageDTO);
            this.initUser(ctx, messageDTO);
        } else if (messageDTO.getAction().equals("heart")) {

        }

    }

    /**
     * @description: 创建连接的时候触发
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception{
        Channel channel = ctx.channel();
        // 保存在线用户
        NettyConfig.getOnlineChannelGroup().add(channel);
        log.info("在线用户数量：{}", NettyConfig.getOnlineChannelGroup().size());
    }

    /**
     * @description: 断开连接触发
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception{
        Channel channel = ctx.channel();
        updateChannel(channel);
        log.info("在线用户数量：{}", NettyConfig.getOnlineChannelGroup().size());
    }

    /**
     * @description: 异常断开
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception{
        Channel channel = ctx.channel();
        updateChannel(ctx.channel());
        log.debug("在线用户数量：{}", NettyConfig.getOnlineChannelGroup().size());
        //当发生异常时，手动关闭Channel
        channel.close();
    }

    /**
     * @description: 连接建好，并且初始化已经完成，可以开始发送和接收消息
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception{
        log.info("websocket inactive : {}", ctx.channel().remoteAddress());
    }

    /***
     * @description: 更新在线人数
     */
    private void updateChannel(Channel channel) {
        NettyConfig.getOnlineChannelGroup().remove(channel);

        Attribute<String> userIdKey = channel.attr(AttributeKey.valueOf(USER_ID));
        String userId = userIdKey.get();
        if (StringUtils.isNotBlank(userId)) {
            NettyConfig.getOnlineUserChannelMap().remove(userId);
        }

        Attribute<String> roleKey = channel.attr(AttributeKey.valueOf(ROLE));
        String role = roleKey.get();
        if (NettyConstant.RECEPTIONIST.equals(role)) {
            SpringContext.getBean(ReceptionistService.class).updateStatus(Long.parseLong(userId), DigitConstant.ZERO);
        } else if (NettyConstant.VISITOR.equals(role)) {
            SpringContext.getBean(SessionService.class).updateStatus(Long.parseLong(userId), DigitConstant.ZERO);
            SpringContext.getBean(VisitorService.class).updateStatus(Long.parseLong(userId), DigitConstant.ZERO);
        }
    }

    /**
     * @description: 发送聊天信息
     */
    private void sendMsg(ChannelHandlerContext ctx, MessageDTO messageDTO) {
        boolean isSuccess = true;
        try {
            // 保存聊天信息
            ChatDTO chatDTO = new ChatDTO();
            chatDTO.setReferenceChatId(Long.parseLong(messageDTO.getReferenceChatId()));
            chatDTO.setContent(messageDTO.getMessage());
            chatDTO.setSessionId(Long.parseLong(messageDTO.getSessionId()));
            chatDTO.setSenderId(Long.parseLong(messageDTO.getSenderId()));
            SpringContext.getBean(ChatService.class).save(chatDTO);
        } catch (Exception e) {
            log.error("发送消息异常：{}", e.getMessage());
            isSuccess = false;
        }

        if (isSuccess) {
            // 发给对方
            Channel channel = NettyConfig.getOnlineUserChannelMap().get(messageDTO.getReceiverId());
            if (Objects.nonNull(channel)) {
                ResultDTO resultDTO = ResultDTO.builder().requestId(messageDTO.getRequestId()).action("chat").data(messageDTO).build();
                String sendMsg = String.format(JSON.toJSONString(resultDTO));
                channel.writeAndFlush(new TextWebSocketFrame(sendMsg));
            }

            // 系统告诉发送者消息发送成功
            ResultDTO resultDTO2 = ResultDTO.builder().requestId(messageDTO.getRequestId()).action("chat").data("ACK").build();
            String sendMsg = String.format(JSON.toJSONString(resultDTO2));
            ctx.channel().writeAndFlush(new TextWebSocketFrame(sendMsg));
        } else {
            // 告诉发送者消息发送失败
            ResultDTO resultDTO = ResultDTO.builder().requestId(messageDTO.getRequestId()).action("chat").data("NACK").build();
            String sendMsg = String.format(JSON.toJSONString(resultDTO));
            ctx.channel().writeAndFlush(new TextWebSocketFrame(sendMsg));
        }
    }

    /**
     * @description: 将连接的客户端注册到服务端中
     */
    private void register(ChannelHandlerContext ctx, MessageDTO messageDTO) {
        Channel channel = ctx.channel();
        AttributeKey<Object> userIdKey = AttributeKey.valueOf(USER_ID);
        AttributeKey<Object> roleKey = AttributeKey.valueOf(ROLE);

        channel.attr(userIdKey).set(messageDTO.getSenderId());
        channel.attr(roleKey).set(messageDTO.getRole());

        NettyConfig.getOnlineUserChannelMap().put(messageDTO.getSenderId(), channel);
    }

    /**
     * @description: 初始化连接的用户
     */
    private void initUser(ChannelHandlerContext ctx, MessageDTO messageDTO) {
        if (NettyConstant.RECEPTIONIST.equals(messageDTO.getRole())) {
            // 客服登录，更新客服状态为在线
            ReceptionistService receptionistService = SpringContext.getBean(ReceptionistService.class);
            receptionistService.updateStatus(Long.parseLong(messageDTO.getSenderId()), DigitConstant.ONE);

            // 初始化成功系统给自己发一条消息
            ResultDTO resultDTO = new ResultDTO();
            resultDTO.setRequestId(messageDTO.getRequestId());
            resultDTO.setAction(messageDTO.getAction());
            resultDTO.setData("success");
            String sendMsg = String.format(JSON.toJSONString(resultDTO));
            ctx.channel().writeAndFlush(new TextWebSocketFrame(sendMsg));

        } else if (NettyConstant.VISITOR.equals(messageDTO.getRole())) {
            // 访客登录,需要分配客服
            // 更新访客状态为在线
            VisitorService visitorService = SpringContext.getBean(VisitorService.class);
            visitorService.updateStatus(Long.parseLong(messageDTO.getSenderId()), DigitConstant.ONE);

            // 获取分配的客服信息
            ReceptionistService receptionistService = SpringContext.getBean(ReceptionistService.class);
            ReceptionistDTO assign = receptionistService.assign();

            // 创建一个会话
            SessionService sessionService = SpringContext.getBean(SessionService.class);
            SessionDTO sessionDTO = new SessionDTO();
            sessionDTO.setReceptionistId(assign.getId());
            sessionDTO.setVisitorId(Long.parseLong(messageDTO.getSenderId()));
            sessionService.save(sessionDTO);

            // 系统给客服发信息，告诉客服有访客上线，需要刷新会话列表
            Channel channel = NettyConfig.getOnlineUserChannelMap().get(String.valueOf(assign.getId()));
            if (Objects.nonNull(channel)) {
                ResultDTO resultDTO = new ResultDTO();
                resultDTO.setRequestId(messageDTO.getRequestId());
                resultDTO.setAction(messageDTO.getAction());
                resultDTO.setData("refresh-list");
                String sendMsg = String.format(JSON.toJSONString(resultDTO));
                channel.writeAndFlush(new TextWebSocketFrame(sendMsg));
            }

            // 系统给访客发送一条初始化成功的消息
            ResultDTO resultDTO2 = new ResultDTO();
            resultDTO2.setRequestId(messageDTO.getRequestId());
            resultDTO2.setAction(messageDTO.getAction());
            resultDTO2.setData(assign);
            String sendMsg2 = String.format(JSON.toJSONString(resultDTO2));
            ctx.channel().writeAndFlush(new TextWebSocketFrame(sendMsg2));
        }
    }

}
